#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <iostream>
#include <string>

#define MAXSIZE 128
#define MAXARGS 32

// shell内部维护的命令行参数表 
char* gargv[MAXARGS];
int gargc = 0;
const char* gsep = " ";
int lastcode = 0;

//环境变量表
char *genv[MAXARGS];
int genvc = 0;



static std::string rfindDir(const std::string& p)
{
    if(p == "/")
      return p;
    const std::string psep = "/";
    auto pos = p.rfind(psep);
    if(pos == std::string::npos)
    {
      return std::string();
    }

    return p.substr(pos + 1);
}

//我们shell自己所处的工作路径
char cwd[MAXSIZE];

const char* GetUserName()
{
  char* name = getenv("USER");
  if(name == NULL)
    return "None";
  return name;
}

const char* GetHostName()
{
  char* hostname = getenv("HOSTNAME");
  if(hostname == NULL)
    return "None";
  return hostname;
}

const char* GetPwd()
{
  
  char* pwd = getenv("PWD");
  //char* pwd = getcwd(cwd,sizeof(cwd)); 
  if(pwd == NULL)
    return "None";
  return pwd;
}

void PrintCommandLine()
{

  printf("[%s@%s %s]#",GetUserName(),GetHostName(),rfindDir(GetPwd()).c_str());
  fflush(stdout);
}

int GetCommand(char command_line[],int size)
{
    
    if(NULL == fgets(command_line, size, stdin))
      return 0;
    command_line[strlen(command_line) - 1] = '\0';
    return strlen(command_line);
}


int ParseCommand(char commandline[])
{
  gargc = 0;
  memset(gargv,0,sizeof(gargv));
  gargv[gargc] = strtok(commandline,gsep);

  while(gargv[++gargc] = strtok(NULL,gsep));

  //printf("gargc:%d\n",gargc);
  int i = 0;
  //for(;i < gargc;i++)
  //{
  //  printf("gargv[%d]: %s\n",i,gargv[i]);
  //}

  return gargc;
}

int ExecuteCommand()
{
  pid_t id = fork(); 
  if(id < 0)
    return -1;

  else if(id == 0)
  {
    //子进程
    execvpe(gargv[0],gargv,genv);
    exit(1);
  }
  else{
    //父进程
    int status = 0;
    pid_t rid = waitpid(id,&status,0);
    if(rid > 0)
    {
      lastcode = WEXITSTATUS(status);
      //printf("wait child process success!\n");
    }
  }
  return 0;
}

//return val:
//0: 不是内建命令
//1: 内建命令&&执行完毕
int CheckBuiltExecyte()
{
  if(strcmp(gargv[0],"cd") == 0)
  {
    //内建命令
    if(gargc == 2)
    {
      //新的目标路径：gargv[1]
      //更改进程中内核的工作路径
      chdir(gargv[1]);
      //更改环境变量
      char pwd[1024];
      getcwd(pwd,sizeof(pwd));
      snprintf(cwd,sizeof(cwd),"PWD=%s",pwd);
      putenv(cwd);
      lastcode = 0;
    }
    return 1;  
  }
  else if(strcmp(gargv[0],"echo") == 0)
  {
    if(gargc == 2)
    {
      if(gargv[1][0] == '$')
      {
        if(strcmp(gargv[1] + 1,"?") == 0)
        {
          printf("lastcode:%d\n",lastcode);
          lastcode = 0;
        }
        else if(strcmp(gargv[1] + 1,"PATH") == 0)
        {
          printf("%s\n",getenv("PATH"));
        }
      }
      return 1;
    }
  }
  return 0;
}

void LoadEnv()
{
  //从父进程拷贝
  
  extern char** environ;
  for(;environ[genvc];genvc++)
  {
    genv[genvc] = malloc(sizeof(char) * 4096);
    strcpy(getenv[genvc],environ[genvc]);
  }
  genv[genvc] = NULL;
}

int main()
{
  //0、从配置文件中获取环境变量表填充环境变量表
  LoadEnv();
  char command_line[MAXSIZE] = {0};
  while(1)
  {
    //1、打印
    PrintCommandLine();
    //2、获取用户输入
    if(0 == GetCommand(command_line, sizeof(command_line)))
      continue;
    //if(command_line[0] == '\0')
    //  continue;
    

    //printf("%s\n",command_line);
    //3、解析字符串
    ParseCommand(command_line);

    //4、判断命令让父进程执行（内建命令）？还是子进程执行？
    if(CheckBuiltExecyte() > 0)
    {
      continue;
    }

    //5、执行命令
    ExecuteCommand();
    //sleep(1);
  }

  return 0;
}
